package com.efeichong.oss;

import com.amazonaws.HttpMethod;
import com.amazonaws.auth.AWSCredentials;
import com.amazonaws.auth.AWSStaticCredentialsProvider;
import com.amazonaws.auth.BasicAWSCredentials;
import com.amazonaws.client.builder.AwsClientBuilder;
import com.amazonaws.services.s3.AmazonS3;
import com.amazonaws.services.s3.AmazonS3ClientBuilder;
import com.amazonaws.services.s3.model.*;
import com.efeichong.exception.BaseException;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import lombok.Cleanup;
import lombok.NonNull;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.joda.time.DateTime;
import org.springframework.web.multipart.MultipartFile;

import java.io.InputStream;
import java.io.OutputStream;
import java.net.URL;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

/**
 * @author lxk
 * @date 2021/11/17
 * @description 亚马逊oss
 */
@Slf4j
public class AmazonOssService extends AbstractOssService {

    public AmazonOssService(@NonNull OssConfigS3 config) {
        super(config);
    }

    @SneakyThrows
    @Override
    public String upload(MultipartFile file, String bucketName, String folderName, @NonNull String suffix) {
        String uuid = UUIDUtils.getId();
        if (!suffix.startsWith(".")) {
            suffix = "." + suffix;
        }
        String newFileName = uuid + suffix;

        AWSCredentials credentials = new BasicAWSCredentials(accessKeyId, accessKeySecret);
        AmazonS3 client = AmazonS3ClientBuilder
                .standard()
                .withCredentials(new AWSStaticCredentialsProvider(credentials))
                .withEndpointConfiguration(new AwsClientBuilder.EndpointConfiguration(endpoint, region))
                .build();

        try {
            ObjectMetadata metadata = new ObjectMetadata();
            metadata.setContentLength(file.getSize());
            metadata.setContentEncoding("UTF-8");
            metadata.setContentType(FileType.getContentType(suffix));

            String key = StringUtils.isBlank(folderName) ? newFileName : folderName + "/" + newFileName;
            PutObjectRequest putObjectRequest = new PutObjectRequest(bucketName, key, file.getInputStream(), metadata);
            client.putObject(putObjectRequest);
            newFileName = "https://" + bucketName + "." + endpoint + "/" + key;
        } finally {
            try {
                //client.shutdown();
            } catch (Exception e) {
                log.error("client关闭失败", e);
                throw new BaseException("client关闭失败");
            }
        }
        return newFileName;
    }

    @SneakyThrows
    @Override
    protected String upload(byte[] bytes, String bucketName, String folderName, String suffix) {
        String uuid = UUIDUtils.getId();
        if (!suffix.startsWith(".")) {
            suffix = "." + suffix;
        }
        String newFileName = uuid + suffix;
        AWSCredentials credentials = new BasicAWSCredentials(accessKeyId, accessKeySecret);
        AmazonS3 client = AmazonS3ClientBuilder
                .standard()
                .withEndpointConfiguration(new AwsClientBuilder.EndpointConfiguration(endpoint, region))
                .withCredentials(new AWSStaticCredentialsProvider(credentials))
                .build();

        InputStream inputStream = null;
        try {
            ObjectMetadata metadata = new ObjectMetadata();
            metadata.setContentLength(bytes.length);
            metadata.setContentEncoding("UTF-8");
            metadata.setContentType(FileType.getContentType(suffix));

            String key = StringUtils.isBlank(folderName) ? newFileName : folderName + "/" + newFileName;
            inputStream = IoUtils.byteToInputStream(bytes);
            PutObjectRequest putObjectRequest = new PutObjectRequest(bucketName, key, inputStream, metadata);
            client.putObject(putObjectRequest);
            newFileName = "https://" + bucketName + "." + endpoint + "/" + key;
        } finally {
            IoUtils.close(inputStream);
            try {
                //client.shutdown();
            } catch (Exception e) {
                log.error("client关闭失败", e);
                throw new BaseException("client关闭失败");
            }
        }
        return newFileName;
    }

    @Override
    public List<String> upload(MultipartFile[] files, String bucketName, String folderName, @NonNull String suffix) {
        List<String> urls = Arrays.stream(files).map(file -> upload(file, bucketName, folderName, suffix)).collect(Collectors.toList());
        return urls;
    }

    @Override
    public UploadUrl getUploadUrl(String bucketName, String folderName, String suffix) {
        if (!suffix.startsWith(".")) {
            suffix = "." + suffix;
        }
        String uuid = UUIDUtils.getId();
        String newFileName = uuid + suffix;
        String key = StringUtils.isBlank(folderName) ? newFileName : folderName + "/" + newFileName;
        AWSCredentials credentials = new BasicAWSCredentials(accessKeyId, accessKeySecret);
        AmazonS3 client = AmazonS3ClientBuilder
                .standard()
                .withCredentials(new AWSStaticCredentialsProvider(credentials))
                .build();
        client.setEndpoint(endpoint);
        URL url;
        try {
            GeneratePresignedUrlRequest request = new GeneratePresignedUrlRequest(bucketName, key, HttpMethod.PUT);
            // 这里设置签名在半个小时后过期
            DateTime expireTime = DateTime.now().plusMinutes(30);
            request.setExpiration(expireTime.toDate());
            request.setContentType(FileType.getContentType(suffix));
            url = client.generatePresignedUrl(request);
            return new UploadUrl(url.toString(), bucketName, endpoint, key);
        } finally {
            try {
                //client.shutdown();
            } catch (Exception e) {
                log.error("client关闭失败", e);
                throw new BaseException("client关闭失败");
            }
        }
    }

    @Override
    public List<UploadUrl> getUploadUrls(String bucketName, String folderName, String suffix, Integer num) {
        AWSCredentials credentials = new BasicAWSCredentials(accessKeyId, accessKeySecret);
        AmazonS3 client = AmazonS3ClientBuilder
                .standard()
                .withCredentials(new AWSStaticCredentialsProvider(credentials))
                .build();
        client.setEndpoint(endpoint);
        try {
            List<UploadUrl> urls = new ArrayList<>();
            for (int i = 0; i < num; i++) {
                String uuid = UUIDUtils.getId();
                String newFileName = uuid + suffix;

                String key = StringUtils.isBlank(folderName) ? newFileName : folderName + "/" + newFileName;
                GeneratePresignedUrlRequest request = new GeneratePresignedUrlRequest(bucketName, key, HttpMethod.PUT);
                request.setContentType(FileType.getContentType(suffix));
                // 这里设置签名在半个小时后过期
                DateTime expireTime = DateTime.now().plusMinutes(30);
                request.setExpiration(expireTime.toDate());
                URL url = client.generatePresignedUrl(request);
                urls.add(new UploadUrl(url.toString(), bucketName, endpoint, key));
            }
            return urls;
        } finally {
            try {
                client.shutdown();
            } catch (Exception e) {
                log.error("client关闭失败", e);
                throw new BaseException("client关闭失败");
            }
        }
    }

    @SneakyThrows
    @Override
    public byte[] download(String url) {
        String bucketName = url.substring(url.indexOf("://") + 3, url.indexOf("."));
        String objectName = url.substring(url.lastIndexOf("/") + 1);
        AWSCredentials credentials = new BasicAWSCredentials(accessKeyId, accessKeySecret);
        AmazonS3 client = AmazonS3ClientBuilder
                .standard()
                .withCredentials(new AWSStaticCredentialsProvider(credentials))
                .withEndpointConfiguration(new AwsClientBuilder.EndpointConfiguration(endpoint, region))
                .build();
        try {
            GetObjectRequest getObjectRequest = new GetObjectRequest(bucketName, objectName);
            S3Object cosObject = client.getObject(getObjectRequest);
            @Cleanup InputStream inputStream = cosObject.getObjectContent();
            return IoUtils.inputStreamToByte(inputStream);
        } finally {
            try {
                //client.shutdown();
            } catch (Exception e) {
                log.error("client关闭失败", e);
                throw new BaseException("client关闭失败");
            }
        }

    }

    @SneakyThrows
    @Override
    public ZipOutputStream batchDownload(OutputStream outputStream, List<String> urls) {
        AWSCredentials credentials = new BasicAWSCredentials(accessKeyId, accessKeySecret);
        AmazonS3 client = AmazonS3ClientBuilder
                .standard()
                .withCredentials(new AWSStaticCredentialsProvider(credentials))
                .withEndpointConfiguration(new AwsClientBuilder.EndpointConfiguration(endpoint, region))
                .build();
        try {
            @Cleanup ZipOutputStream zos = new ZipOutputStream(outputStream);
            urls.forEach(FunctionWithException.wrapper(url -> {
                String bucketName = url.substring(url.indexOf("://") + 3, url.indexOf("."));
                String objectName = url.substring(url.lastIndexOf("/") + 1);
                GetObjectRequest getObjectRequest = new GetObjectRequest(bucketName, objectName);
                S3Object ossObject = client.getObject(getObjectRequest);
                @Cleanup InputStream inputStream = ossObject.getObjectContent();
                byte[] buf = new byte[1024];
                zos.putNextEntry(new ZipEntry(url.substring(url.lastIndexOf("/") + 1)));
                int len;
                while ((len = inputStream.read(buf)) != -1) {
                    zos.write(buf, 0, len);
                }
                zos.closeEntry();
            }));
            return zos;
        } finally {
            try {
                //client.shutdown();
            } catch (Exception e) {
                log.error("client关闭失败", e);
                throw new BaseException("client关闭失败");
            }
        }
    }

    @Override
    public void delete(String url) {
        String bucketName = url.substring(url.indexOf("://") + 3, url.indexOf("."));
        String objectName = url.substring(url.lastIndexOf("/") + 1);
        AWSCredentials credentials = new BasicAWSCredentials(accessKeyId, accessKeySecret);
        AmazonS3 client = AmazonS3ClientBuilder
                .standard()
                .withCredentials(new AWSStaticCredentialsProvider(credentials))
                .withEndpointConfiguration(new AwsClientBuilder.EndpointConfiguration(endpoint, region))
                .build();
        try {
            client.deleteObject(bucketName, objectName);
        } finally {
            try {
                //client.shutdown();
            } catch (Exception e) {
                log.error("client关闭失败", e);
                throw new BaseException("client关闭失败");
            }
        }
    }

    @Override
    public void batchDelete(List<String> urls) {
        Map<String, List<DeleteObjectsRequest.KeyVersion>> bucketUrlMap = Maps.newLinkedHashMap();
        for (String url : urls) {
            String bucketName = url.substring(url.indexOf("://") + 3, url.indexOf("."));
            String objectName = url.substring(url.lastIndexOf("/") + 1);
            if (bucketUrlMap.get(bucketName) != null) {
                bucketUrlMap.get(bucketName).add(new DeleteObjectsRequest.KeyVersion(objectName));
            } else {
                bucketUrlMap.put(bucketName, Lists.newArrayList(new DeleteObjectsRequest.KeyVersion(objectName)));
            }
        }
        AWSCredentials credentials = new BasicAWSCredentials(accessKeyId, accessKeySecret);
        AmazonS3 client = AmazonS3ClientBuilder
                .standard()
                .withCredentials(new AWSStaticCredentialsProvider(credentials))
                .withEndpointConfiguration(new AwsClientBuilder.EndpointConfiguration(endpoint, region))
                .build();
        try {
            for (Map.Entry<String, List<DeleteObjectsRequest.KeyVersion>> entry : bucketUrlMap.entrySet()) {
                DeleteObjectsRequest request = new DeleteObjectsRequest(entry.getKey());
                request.setKeys(entry.getValue());
                client.deleteObjects(request);
            }
        } finally {
            try {
                //client.shutdown();
            } catch (Exception e) {
                log.error("client关闭失败", e);
                throw new BaseException("client关闭失败");
            }
        }
    }
}
